終於寫道flow的最後一篇了
儘管我們在自己的開發上,能夠高興地寫coroutine和suspend,但人生總是不那麼美好,我們會在各式sdk或套件裡遇到callback,難道我們只能認命,乖乖地用callback完成那個功能
nono,coroutine的開發者做出了一個callbackfFlow的東西,可以將callback的值轉成flow使用
@ExperimentalCoroutinesApi
val callFlowResult = callbackFlow<Post> {
repo.postCallback.enqueue(object :Callback<Post>{
override fun onResponse(call: Call<Post>, response: Response<Post>) {
trySend(response.body()!!)
}
override fun onFailure(call: Call<Post>, t: Throwable) {
close(t)
}
})
awaitClose{
// clean up when Flow collection ends
}
}
注意,網路上的每個範例,都會長得有點不同,以官方的其中一個來說
val locationsSource: Flow<Location> = callbackFlow<Location> {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult?) {
result ?: return
try { offer(result.lastLocation) } catch(e: Exception) {}
}
}
requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper())
.addOnFailureListener { e ->
close(e) // in case of exception, close the Flow
}
// clean up when Flow collection ends
awaitClose {
removeLocationUpdates(callback)
}
}
差很多吧,但你品,你細品,就是成功收到值時,offer(現在棄用,改用trysend),失敗時close
正常結束時,awaitclose釋放資源
Internally, callbackFlow uses a channel, which is conceptually very similar to a blocking queue, and has a default capacity of 64 elements.
而callbackFlow底層是以channel去實作的,channel的特性就像是一個queue,如果沒特別設置那預設的上限數是64。
lifecycleScope.launchWhenStarted suspends the execution of the coroutine. New locations are not processed, but the callbackFlow producer keeps sending locations nonetheless. Using the lifecycleScope.launch or launchIn APIs are even more dangerous as the view keeps consuming locations even if it’s in the background! Which could potentially make your app crash.
在ui層蒐集callackflow有說道launchWhenStart會在背景仍繼續執行,我當時也是充滿疑惑,因為flow不會channel也不會,所以又寫了一些code來驗證
最後得知,channel如果是用offer()的話,會有這種情況,但offer已經被棄用了,所以目前測試send()並不會在背景執行,而callbackFlow既然是以channel去實作,自然也會有相同的情況,但大家用channel記得去關掉喔
我也不敢把話講太死,畢竟目前的官方資料還是以repeatOnLifecycle()為主,也提過launchWhenStart在一些intermediary的操作上還是會造成背景執行,如果有人看到更新的,歡迎在留言補充
essons-learnt-using-coroutines-flow
a-safer-way-to-collect-flows-from-android-ui